package registry

import (
	"context"
	"fmt"
	"github.com/sirupsen/logrus"
	etcdv3 "go.etcd.io/etcd/client/v3"
	"go.etcd.io/etcd/client/v3/naming/endpoints"
	"regexp"
	"time"
)

type Service struct {
	Project string
	Name    string
	Address string
	Replica int
	MType   string
	SType   []string
}

func MustRegistry(ctx context.Context, client *etcdv3.Client, srv Service) {
	var (
		ok        bool
		err       error
		grantResp *etcdv3.LeaseGrantResponse
		lease     etcdv3.Lease
		leaseCh   <-chan *etcdv3.LeaseKeepAliveResponse
		manager   endpoints.Manager
	)

	if srv.Name == "" {
		logrus.Panic("must declare service name")
	}

	if srv.Project == "" {
		logrus.Warnf("service project name empty, inited to '%s'", "shenrong")
		srv.Project = "shenrong"
	}

	if srv.Replica <= 0 {
		logrus.Warnf("service replica invalid, inited to '%d'", 1)
		srv.Replica = 1
	}

	if ok, err = regexp.MatchString(
		`^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]):((6553[0-5])|(655[0-2]\d)|(65[0-4]\d{2})|(6[0-4]\d{3})|([1-5]\d{4})|([0-5]{0,5})|(\d{1,4}))$`,
		srv.Address,
	); err != nil || !ok {
		logrus.Panicf("service address invalid: %s, %v, only accept ipv4:port", srv.Address, err)
	}

	hostname := fmt.Sprintf("resolver/%s/grpc/%s/%s/%d", srv.Project, srv.MType, srv.Name, srv.Replica)

	gctx, _ := context.WithTimeout(ctx, 5*time.Second)
	if grantResp, err = client.Grant(gctx, 20); err != nil {
		logrus.Panicf("etcd grant err: %v", err)
	}

	lease = etcdv3.NewLease(client)

	if leaseCh, err = lease.KeepAlive(ctx, grantResp.ID); err != nil {
		logrus.Panicf("etcd lease keep alive err: %v", err)
	}

	if manager, err = endpoints.NewManager(client, fmt.Sprintf("resolver/%s/grpc/%s/%s", srv.Project, srv.MType, srv.Name)); err != nil {
		logrus.Panicf("etcd new endpoints manager err: %v", err)
	}

	actx, _ := context.WithTimeout(ctx, 5*time.Second)
	if err = manager.AddEndpoint(actx, hostname,
		endpoints.Endpoint{
			Addr: srv.Address,
			Metadata: map[string]interface{}{
				"project": srv.Project,
				"name":    srv.Name,
				"mtype":   srv.MType,
				"stype":   srv.SType,
				"replica": srv.Replica,
			}},
		etcdv3.WithLease(grantResp.ID),
	); err != nil {
		logrus.Panicf("etcd add endpoints err: %v", err)
	}

	go func() {
		defer func() {
			dctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
			if _, err := client.Delete(dctx, hostname); err != nil {
				logrus.Warnf("del key to unregistry %s err: %v", hostname, err)
			}

			logrus.Warnf("module[%s] %s unregistry", srv.Name, hostname)

			logrus.Fatal("etcd keep_alive lease ch closed")
		}()

		for keepAliveResp := range leaseCh {
			_ = keepAliveResp
		}
	}()

	logrus.Infof("[grpc:%s] [key: %s] [%s:%s:%d] [%s] registry success", srv.Project, hostname, srv.MType, srv.Name, srv.Replica, srv.Address)
}
